
#pragma once
#include "ly/cell.hpp"
#include "ly/layout.hpp"
#include "ly/types.hpp"
#include "ly/variant.hpp"

namespace ly {
class PCellVariant;

typedef std::vector<Variant> pcell_parameters_type;
struct PCellParametersCompareFunc {
  bool operator()(const pcell_parameters_type* a, const pcell_parameters_type* b) const;
};

class PCellParameterDeclaration {
 public:
  /**
   *  @brief A enum describing the type
   */
  enum type {
    t_int,       //  an integer value
    t_double,    //  a floating-point value
    t_string,    //  a string value
    t_boolean,   //  a boolean value
    t_layer,     //  a layer (value is a db::LayerProperties object)
    t_shape,     //  a shape (a db::Point, db::Box, db::Polygon, db::Edge or
                 //  db::Path) rendering a guiding shape
    t_list,      //  a list of strings
    t_callback,  //  callback only (button)
    t_none       //  no specific type
  };

  /**
   *  @brief The default constructor
   */
  PCellParameterDeclaration() : m_hidden(false), m_readonly(false), m_type(t_none) {}

  /**
   *  @brief The constructor with a name
   */
  PCellParameterDeclaration(const std::string& name) : m_hidden(false), m_readonly(false), m_type(t_none), m_name(name) {}

  /**
   *  @brief The constructor with a name, type and description
   */
  PCellParameterDeclaration(const std::string& name, type t, const std::string& description)
      : m_hidden(false), m_readonly(false), m_type(t), m_name(name), m_description(description) {}

  /**
   *  @brief The constructor with a name, type, description and default value
   */
  PCellParameterDeclaration(const std::string& name, type t, const std::string& description, const Variant& def)
      : m_default(def), m_hidden(false), m_readonly(false), m_type(t), m_name(name), m_description(description) {}

  /**
   *  @brief The constructor with a name, type, description, default value and
   * unit
   */
  PCellParameterDeclaration(const std::string& name, type t, const std::string& description, const Variant& def, const std::string& unit)
      : m_default(def), m_hidden(false), m_readonly(false), m_type(t), m_name(name), m_description(description), m_unit(unit) {}

  /**
   *  @brief The constructor with a name, type and description and choice values
   * / choice descriptions
   */
  PCellParameterDeclaration(const std::string& name, type t, const std::string& description, const std::vector<Variant>& choices,
                            const std::vector<std::string>& choice_descriptions)
      : m_choices(choices),
        m_choice_descriptions(choice_descriptions),
        m_hidden(false),
        m_readonly(false),
        m_type(t),
        m_name(name),
        m_description(description) {}

  /**
   *  @brief Getter for the name property
   */
  const std::string& get_name() const { return m_name; }

  /**
   *  @brief Setter for the name property
   */
  void set_name(const std::string& name) { m_name = name; }

  /**
   *  @brief Getter for the unit property
   */
  const std::string& get_unit() const { return m_unit; }

  /**
   *  @brief Setter for the unit property
   */
  void set_unit(const std::string& unit) { m_unit = unit; }

  /**
   *  @brief Getter for the description property
   */
  const std::string& get_description() const { return m_description; }

  /**
   *  @brief Setter for the description property
   */
  void set_description(const std::string& description) { m_description = description; }

  /**
   *  @brief Getter for the type property
   */
  type get_type() const { return m_type; }

  /**
   *  @brief Setter for the type property
   */
  void set_type(type t) { m_type = t; }

  /**
   *  @brief Getter for the readonly property
   */
  bool is_readonly() const { return m_readonly; }

  /**
   *  @brief Setter for the readonly property
   */
  void set_readonly(bool readonly) { m_readonly = readonly; }

  /**
   *  @brief Getter for the hidden property
   */
  bool is_hidden() const { return m_hidden; }

  /**
   *  @brief Setter for the hidden property
   */
  void set_hidden(bool hidden) { m_hidden = hidden; }

  /**
   *  @brief Getter for the default value
   */
  const Variant& get_default() const { return m_default; }

  /**
   *  @brief Setter for the default value
   */
  void set_default(const Variant& def) { m_default = def; }

  /**
   *  @brief Getter for the choices
   */
  const std::vector<Variant>& get_choices() const { return m_choices; }

  /**
   *  @brief Setter for the choices
   */
  void set_choices(const std::vector<Variant>& choices) { m_choices = choices; }

  /**
   *  @brief Getter for the choice descriptions
   *
   *  The choice descriptions correspond to choice values. The descriptions
   *  can be shown instead of the values in a drop-down box for parameters with
   *  discrete values.
   */
  const std::vector<std::string>& get_choice_descriptions() const { return m_choice_descriptions; }

  /**
   *  @brief Setter for the choice descriptions
   */
  void set_choice_descriptions(const std::vector<std::string>& choice_descriptions) { m_choice_descriptions = choice_descriptions; }

  /**
   *  @brief Equality
   */
  bool operator==(const PCellParameterDeclaration& d) const {
    return m_choices == d.m_choices && m_choice_descriptions == d.m_choice_descriptions && m_default == d.m_default && m_hidden == d.m_hidden &&
           m_readonly == d.m_readonly && m_type == d.m_type && m_name == d.m_name && m_description == d.m_description && m_unit == d.m_unit;
  }

 private:
  std::vector<Variant> m_choices;
  std::vector<std::string> m_choice_descriptions;
  Variant m_default;
  bool m_hidden, m_readonly;
  type m_type;
  std::string m_name;
  std::string m_description, m_unit;
};

class PCellDeclaration {
 public:
  PCellDeclaration(/* args */) {};
  ~PCellDeclaration() {};

 private:
  int m_ref_count;
  pcell_id_type m_id;
  std::string m_name;
  Layout* mp_layout;
  mutable bool m_has_parameter_declarations;
  mutable std::vector<PCellParameterDeclaration> m_parameter_declarations;

  //   friend class db::Layout;
};

class PCellHeader {
 public:
  typedef std::map<const pcell_parameters_type*, PCellVariant*, PCellParametersCompareFunc> variant_map_t;
  PCellHeader(/* args */) {};
  ~PCellHeader() {};

 private:
  variant_map_t m_variant_map;
  PCellDeclaration* mp_declaration;
  size_t m_pcell_id;
  std::string m_name;
};

class PCellVariant : Cell {
 public:
  PCellVariant(/* args */) {};
  ~PCellVariant() {};

 private:
  pcell_parameters_type m_parameters;
  mutable std::string m_display_name;
  pcell_id_type m_pcell_id;
  bool m_registered;
};

}  // namespace ly
